
/*
 ** server.c -- a stream socket server demo
 */
#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include "globalFunctions.h"
#include "forkServer.h"
#include "FileSystemOperator.h"




int StartForkServer(int protocol)
{
    if ( protocol == 1 )
    {
        return StartForkServer_tcp();
    }
    else
    {
        return StartForkServer_udp();
    }
}

int StartForkServer_tcp()
{
    int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
    struct addrinfo hints, *servinfo, *p;
    struct sockaddr_storage their_addr; // connector's address information
    socklen_t sin_size;
    struct sigaction sa;
    int yes = 1;
    char buf[BUFSIZE]; /* message buffer */
    char s[INET6_ADDRSTRLEN];
    int rv;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE; // use my IP

    if ( (rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0 )
    {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and bind to the first we can
    for ( p = servinfo; p != NULL; p = p->ai_next )
    {
        if ( (sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1 )
        {
            perror("server: socket");
            continue;
        }

        if ( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
                sizeof (int)) == -1 )
        {
            perror("setsockopt");
            exit(1);
        }

        if ( bind(sockfd, p->ai_addr, p->ai_addrlen) == -1 )
        {
            close(sockfd);
            perror("server: bind");
            continue;
        }

        break;
    }

    if ( p == NULL )
    {
        fprintf(stderr, "server: failed to bind\n");
        return 2;
    }

    freeaddrinfo(servinfo); // all done with this structure

    if ( listen(sockfd, BACKLOG) == -1 )
    {
        perror("listen");
        exit(1);
    }

    sa.sa_handler = sigchld_handler; // reap all dead processes
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    if ( sigaction(SIGCHLD, &sa, NULL) == -1 )
    {
        perror("sigaction");
        exit(1);
    }

    printf("server: waiting for connections...\n");

    while ( 1 )
    { // main accept() loop
        sin_size = sizeof their_addr;
        new_fd = accept(sockfd, (struct sockaddr *) &their_addr, &sin_size);
        if ( new_fd == -1 )
        {
            perror("accept");
            continue;
        }

        inet_ntop(their_addr.ss_family,
                get_in_addr((struct sockaddr *) &their_addr),
                s, sizeof s);
        printf("server: got connection from %s\n", s);

        //if ( !fork() )
        { // this is the child process
            //been here
            close(sockfd); // child doesn't need the listener
            
            int nReturnStatus = send(new_fd, "Ready to accept command", BUFSIZE, 0);
            
            
            if (  nReturnStatus == -1)
            {
                  perror("send");
            }
            
            
            bzero(buf, BUFSIZE);
            int n = read(new_fd, buf, BUFSIZE);            
            if (n < 0) 
                 {
                  perror("send");
            }
            else
            {
                fileManager_TCP(new_fd,buf);
            }
             
            close(new_fd);
            //exit(0);
        }
        close(new_fd); // parent doesn't need this
    }

    return 0;
}

int StartForkServer_udp()
{
    int sockfd;
    struct addrinfo hints, *servinfo, *p;
    int rv;
    int numbytes;
    struct sockaddr_storage their_addr;
    char buf[MAXBUFLEN];
    socklen_t addr_len;
    char s[INET6_ADDRSTRLEN];

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_flags = AI_PASSIVE; // use my IP

    if ( (rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0 )
    {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and bind to the first we can
    for ( p = servinfo; p != NULL; p = p->ai_next )
    {
        if ( (sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1 )
        {
            perror("listener: socket");
            continue;
        }

        if ( bind(sockfd, p->ai_addr, p->ai_addrlen) == -1 )
        {
            close(sockfd);
            perror("listener: bind");
            continue;
        }

        break;
    }

    if ( p == NULL )
    {
        fprintf(stderr, "listener: failed to bind socket\n");
        return 2;
    }

    freeaddrinfo(servinfo);

    printf("listener: waiting to recvfrom...\n");

    addr_len = sizeof their_addr;
    if ( (numbytes = recvfrom(sockfd, buf, MAXBUFLEN - 1, 0,
            (struct sockaddr *) &their_addr, &addr_len)) == -1 )
    {
        perror("recvfrom");
        exit(1);
    }

    printf("listener: got packet from %s\n",
            inet_ntop(their_addr.ss_family,
            get_in_addr((struct sockaddr *) &their_addr),
            s, sizeof s));
    printf("listener: packet is %d bytes long\n", numbytes);
    buf[numbytes] = '\0';
    printf("listener: packet contains \"%s\"\n", buf);

    close(sockfd);

    return 0;
}
